package org.zh.sh.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;

import static org.zh.sh.config.GlobalStatic.classPath;
import static org.zh.sh.config.GlobalStatic.staticFile;

public class ComponentScan {
    private static final String META_INF = "META-INF";
    private static final String module_info = "module-info.class";
    private static final String package_info = "package-info.class";
    private static final String $ = "$";
    private static final String clazz = ".class";
    private static final String url_separator = "/";
    private static final String DOT = ".";
    private static final String jar = ".jar";
    private static final String classes = "classes\\";
    private static final String staticPath = "static";
    private static final Boolean isWindows = System.getProperty("os.name").contains("Windows");
    private static final String[] allJar = System.getProperty(classPath).split(File.pathSeparator);

    public static List<Class<?>> componentScan(Class<?> mainClass) throws IOException, ClassNotFoundException {

        List<Class<?>> classList = new ArrayList<>();
        String packageName = mainClass.getPackageName();
        for (String jarPath : allJar) {
            if (jarPath.endsWith(jar) && !packageName.isEmpty()) {
                String packagePath = packageName.replace(DOT, url_separator);
                JarFile jarFile = new JarFile(jarPath);
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry jarEntry = entries.nextElement();
                    String name = jarEntry.getName();
                    if (!jarEntry.isDirectory() && !name.contains(META_INF) && !name.endsWith(module_info) && !name.endsWith(package_info) && !name.contains($)) {
                        if (name.startsWith(packagePath)) {
                            String className = name.substring(0, name.lastIndexOf(clazz)).replace(url_separator, DOT);
                            Class<?> aClass = Class.forName(className);
                            classList.add(aClass);
                        }
                    }
                }
                jarFile.close();
            } else {
                String packagePath = packageName.replace(DOT, "\\");
                Stream<Path> walk = Files.walk(Paths.get(jarPath));
                walk.filter(path -> {
                            String name = path.toString();
                            return !name.endsWith("\\")
                                   && !name.contains(META_INF)
                                   && !name.endsWith(module_info)
                                   && !name.endsWith(package_info)
                                   && name.endsWith(clazz)
                                   && !name.contains($)
                                   && name.indexOf(packagePath) > 0;
                        })
                        .map(path -> {
                            String ps = path.toString();
                            return ps.substring(ps.indexOf(classes) + classes.length(),
                                            ps.length() - clazz.length())
                                    .replace("\\", DOT);
                        })
                        .map(clazz -> {
                            try {
                                return Class.forName(clazz);
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                        }).forEach(classList::add);
                walk.close();
            }
        }
        return classList;
    }

    public static void staticFileBytes() throws IOException {
        String rootPath = allJar[0];
        if (rootPath.endsWith(jar)) {
            JarFile jarFile = new JarFile(rootPath);
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                if (!jarEntry.isDirectory() && name.startsWith(staticPath)) {
                    byte[] bytes = jarFile.getInputStream(jarEntry).readAllBytes();
                    staticFile.put(name.substring(staticPath.length()), bytes);
                }
            }
            jarFile.close();
        } else {
            Path staticAllPath = Paths.get(rootPath, staticPath);
            Stream<Path> walk = Files.walk(staticAllPath);
            walk.filter(path1 -> !Files.isDirectory(path1)).forEach(filePath -> {
                try {
                    int i = filePath.toString().indexOf(staticPath) + staticPath.length();
                    if (isWindows) {
                        staticFile.put(filePath.toString().substring(i).replace(File.separator, "/"), Files.readAllBytes(filePath));
                    } else {
                        staticFile.put(filePath.toString().substring(i), Files.readAllBytes(filePath));
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            walk.close();
        }
    }
}
